#include "max7219_dot_matrix.h"

// the registers for the MAX7221 and MAX7219
#define REG_DECODE        0x09                        // "decode mode" register
#define REG_INTENSITY     0x0a                        // "intensity" register
#define REG_SCAN_LIMIT    0x0b                        // "scan limit" register
#define REG_SHUTDOWN      0x0c                        // "shutdown" register
#define REG_DISPLAY_TEST  0x0f                        // "display test" register

static u8 moduleDisp[64];
static u8 maxDevices;
static u8 spidata[16];

static void max7219_Write (uint addr, u8 reg_number, u8 dataout) {
    //Create an array with the data to shift out
    int offset=addr*2;
    int maxbytes=maxDevices*2;
    int i; for(i=0;i<maxbytes;i++) spidata[i]=0;
    //put our device data into the array
    spidata[offset+1] = reg_number;
    spidata[offset] = dataout;

    max7219_Enable();
    for(i=maxbytes;i>0;i--)
    	max7219_TransferByte(spidata[i-1]); // write register number to MAX7219
  	max7219_Disable();
}

//------------------------------------------------------------------------------------------------------------------
// Descrição:	Inicializa pinos do controlador e o driver controlador
//				O acesso é feito via barramento SPI no modo padrão com velocidade máxima de 10MHz
// Parametros	nModule: Quantidade de módulos em cascata deve ser gerencido
// Retorna: 	Nada
//------------------------------------------------------------------------------------------------------------------
int max7219_Init(uint nDevices) {
	if ( (nDevices < 1) || (nDevices > 8)) return errDEVICE_INVALID;
	maxDevices = nDevices;
	max7219_SetupPorts();	// Configurar os pinos de controle

	int x; for(x=0;x<64;x++) moduleDisp[x] = 0;
	for(x=0;x<maxDevices;x++) {
		max7219_Write(x, REG_DISPLAY_TEST, 0);              // Wtihout test
		max7219_SetScanLimit(x,7);							// Scanlimit is set to max on startup
		max7219_Write(x, REG_DECODE, 0x00);                 // set to "no decode" for all digits
		max7219_Shutdown(x, pdFALSE);                       // select normal operation (i.e. not shutdown)
		max7219_DisplayTest(x, pdFALSE);                    // select normal operation (i.e. not test mode)
		max7219_Clear(x);                                   // clear all digits
		max7219_SetBrightness(x, max7219_INTENSITY_MAX);    // set to maximum intensity
	}

  	return pdPASS;
}

int max7219_SetScanLimit(uint addr, uint limit) {
    if (addr>=maxDevices) return errDEVICE_INVALID;
    if (limit>7) return errDEVICE_VALUE_OUT_OF_RANGE;
   	max7219_Write(addr, REG_SCAN_LIMIT, limit);
    return pdPASS;
}

//------------------------------------------------------------------------------------------------------------------
// Descrição:	Tira o display no modo de desligado
// Parametros	pdTRUE: Fa o desligamento do display
//				pdFALSE: Reativa o display
// Retorna: 	Nada
//------------------------------------------------------------------------------------------------------------------
int max7219_Shutdown(uint addr, u8 st) {
	if (addr>=maxDevices) return errDEVICE_INVALID;
	if (st) max7219_Write(addr, REG_SHUTDOWN, 0);	// put MAX7219 into "shutdown" mode
	else max7219_Write(addr, REG_SHUTDOWN, 1);  	// put MAX7219 into "normal" mode
	return pdPASS;
}

//------------------------------------------------------------------------------------------------------------------
// Descrição:	Inicia o procedimento de teste no display
// Parametros	pdTRUE: Inicia o procedimento de auto teste
//				pdFALSE: Desliga o procedimento de auto teste
// Retorna: 	Nada
//------------------------------------------------------------------------------------------------------------------
int max7219_DisplayTest(uint addr, u8 st) {
  	if (addr>=maxDevices) return errDEVICE_INVALID;
  	if (st > 1) st = 1;
  	max7219_Write(addr, REG_DISPLAY_TEST, st);                 // put MAX7219 into "display test" mode
  	return pdPASS;
}

//------------------------------------------------------------------------------------------------------------------
// Descrição:	Ajusta a itensidade do display
// Parametros	Brilho de 0 a 0xf
// Retorna: 	Nada
//------------------------------------------------------------------------------------------------------------------
int max7219_SetBrightness(uint addr, u8 brightness) {
  	if (addr>=maxDevices) return errDEVICE_INVALID;
  	brightness &= 0x0f;                                 // mask off extra bits
  	max7219_Write(addr, REG_INTENSITY, brightness);           // set brightness
  	return pdPASS;
}

//------------------------------------------------------------------------------------------------------------------
// Descrição:	Limpa todos os displays
// Parametros	Nenhum
// Retorna: 	Nada
//------------------------------------------------------------------------------------------------------------------
int max7219_Clear(uint addr) {
	if (addr>=maxDevices) return errDEVICE_INVALID;
	int offset=addr*8;
  	u8 i; for (i=0; i<8; i++) {
   		moduleDisp[offset+i]=0;
        max7219_Write(addr, i+1,moduleDisp[offset+i]);
    }
  	return pdPASS;
}


int max7219_SetLed(uint addr, uint row, uint column, int state) {
    if(addr>=maxDevices) return errDEVICE_INVALID;
    if((row>7) || (column>7)) return errDEVICE_VALUE_OUT_OF_RANGE;

    int offset;
    u8 val=0x00;

    offset=addr*8;
    val= 0x80 >> column;
    if (state) moduleDisp[offset+row]=moduleDisp[offset+row]|val;
    else {
        val=~val;
        moduleDisp[offset+row]=moduleDisp[offset+row]&val;
    }
    max7219_Write(addr, row+1,moduleDisp[offset+row]);
    return pdPASS;
}


int max7219_SetRow(uint addr, uint row, u8 value) {
    if (addr>=maxDevices) return errDEVICE_INVALID;
    if (row>7) return errDEVICE_VALUE_OUT_OF_RANGE;

    int offset;
    offset=addr*8;
    moduleDisp[offset+row]=value;
    max7219_Write(addr, row+1,moduleDisp[offset+row]);

    return pdPASS;
}

int max7219_SetColumn(uint addr, uint col, u8 value) {
    if (addr>=maxDevices) return errDEVICE_INVALID;
    if (col>7) return errDEVICE_VALUE_OUT_OF_RANGE;

    u8 val;
    uint row; for(row=0;row<8;row++) {
        val=value >> (7-row);
        val=val & 0x01;
        max7219_SetLed(addr, row, col, val);
    }

   return pdPASS;
}

/*
 * Segments to be switched on for characters and digits on
 * 7-Segment Displays
 */
const u8 charTable [] = {
    0b01111110,0b00110000,0b01101101,0b01111001,0b00110011,0b01011011,0b01011111,0b01110000,
    0b01111111,0b01111011,0b01110111,0b00011111,0b00001101,0b00111101,0b01001111,0b01000111,
    0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,
    0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,
    0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,
    0b00000000,0b00000000,0b00000000,0b00000000,0b10000000,0b00000001,0b10000000,0b00000000,
    0b01111110,0b00110000,0b01101101,0b01111001,0b00110011,0b01011011,0b01011111,0b01110000,
    0b01111111,0b01111011,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,
    0b00000000,0b01110111,0b00011111,0b00001101,0b00111101,0b01001111,0b01000111,0b00000000,
    0b00110111,0b00000000,0b00000000,0b00000000,0b00001110,0b00000000,0b00000000,0b00000000,
    0b01100111,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,
    0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00001000,
    0b00000000,0b01110111,0b00011111,0b00001101,0b00111101,0b01001111,0b01000111,0b00000000,
    0b00110111,0b00000000,0b00000000,0b00000000,0b00001110,0b00000000,0b00010101,0b00011101,
    0b01100111,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,
    0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000,0b00000000
};

int max7219_SetDigit(uint addr, int digit, u8 value, u8 dp) {
    if (addr>=maxDevices) return errDEVICE_INVALID;
	if ( (digit<0) || (digit>7) || (value>15)) return errDEVICE_VALUE_OUT_OF_RANGE;

    int offset = addr*8;
    u8 v = charTable[value];
    if(dp) v |= 0x80;

    moduleDisp[offset+digit]=v;
    max7219_Write(addr, digit+1,v);

    return pdPASS;
}

// imprime um valor no display no formato 7seg
// addr modulo max7219 a ser acessado, começando de 0 ao máximo
// digit: digito do módulo
// value: valor em ascii
// dp: Sinaliza para ligar ou não o ponto decimal
int max7219_SetChar(int addr, int digit, char value, u8 dp) {
    if (addr>=maxDevices) return errDEVICE_INVALID;
	if ( (digit<0) || (digit>7) ) return errDEVICE_VALUE_OUT_OF_RANGE;

    int offset=addr*8;
    u8 index=(u8)value;
    if(index >127) index=32; // no defined beyond index 127, so we use the space char
    u8 v = charTable[index];
    if(dp) v|=0b10000000;

    moduleDisp[offset+digit]=v;
    max7219_Write(addr, digit+1,v);

    return pdPASS;
}
